#include <SDL/SDL.h>
#include <cstdio>
#include <fstream>
#include <map>
#include <string>
#include <boost/format.hpp>

using boost::format;

static const char colchars[] = " .*!#$%&'()+,-/:;<=>?@[]^_`{|}~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";

namespace {
  Uint32 pixeldata2rgb(const SDL_PixelFormat *fmt, Uint32 pixel) {
    Uint8 red, green, blue;
    Uint32 temp;
    /* Get Red component */
    temp = pixel & fmt->Rmask; /* Isolate red component */
    temp = temp >> fmt->Rshift;/* Shift it down to 8-bit */
    temp = temp << fmt->Rloss; /* Expand to a full 8-bit number */
    red = (Uint8)temp;
    /* Get Green component */
    temp = pixel & fmt->Gmask; /* Isolate green component */
    temp = temp >> fmt->Gshift;/* Shift it down to 8-bit */
    temp = temp << fmt->Gloss; /* Expand to a full 8-bit number */
    green = (Uint8)temp;
    /* Get Blue component */
    temp = pixel & fmt->Bmask; /* Isolate blue component */
    temp = temp >> fmt->Bshift;/* Shift it down to 8-bit */
    temp = temp << fmt->Bloss; /* Expand to a full 8-bit number */
    blue = (Uint8)temp;
    return ((Uint32)red << 16) | ((Uint32)green << 8) | blue;
  }
};

extern "C" void writeXPM(SDL_Surface *surf, const char *fname) {
  std::string buf;
  std::map<Uint32, char> colmap;
  std::map<Uint32, char>::const_iterator it;
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
  const Uint32 rmask = 0xff000000;
  const Uint32 gmask = 0xff0000;
  const Uint32 bmask = 0xff00;
  //const Uint32 amask = 0xff;
#else
  const Uint32 rmask = 0xff;
  const Uint32 gmask = 0xff00;
  const Uint32 bmask = 0xff0000;
#endif

  //SDL_Surface *dst = SDL_CreateRGBSurface(surf->flags, surf->w, surf->h, surf->format->BitsPerPixel, rmask, gmask, bmask, 0);
  SDL_Surface *dst = SDL_CreateRGBSurface(surf->flags, surf->w, surf->h, 32, rmask, gmask, bmask, 0);
  if(dst != NULL) {
    SDL_BlitSurface(surf, NULL, dst, NULL);
    SDL_LockSurface(dst);
    Uint8 *dataptr = static_cast<Uint8 *>(dst->pixels);
    for(int y = 0; y < dst->h; ++y) {
      Uint32 *data = (Uint32 *)(dataptr + y * dst->pitch);
      buf += '"';
      for(int x = 0; x < dst->w; ++x) {
	Uint32 colour_RGB = pixeldata2rgb(dst->format, data[x]);
	if((it = colmap.find(colour_RGB)) == colmap.end()) {
	  char c = colchars[colmap.size() % sizeof(colchars)];
	  colmap[colour_RGB] = c;
	  buf += c;
	} else {
	  buf += it->second;
	}
      }
      buf += "\",\n";
    }
    std::ofstream out(fname);
    if(out) {
      out << "/* XPM */\n"
	  << "static char *filename[] = {\n"
	  << '"' << surf->w << ' ' << surf->h << ' ' << colmap.size() << " 1\",\n";
      for(it = colmap.begin(); it != colmap.end(); ++it) {
	if(it->first == 0xffffff /*todo: make variable*/) {
	  out << format("\"%c c None\",\n") % it->second;
	} else {
	  out << format("\"%c c #%06x\",\n") % it->second % it->first;
	}
      }
      out << buf << "};\n";
    }
    SDL_UnlockSurface(dst);
    SDL_FreeSurface(dst);
  }
}
/*
  g++ -c -Wall -O2 writeXPM.cc 
  ghc --make -O2 -Wall -lstdc++ entropyCalc.hs writeXPM.o
*/
